﻿using System;
using System.Reflection;
using UnityEditor;
using UnityEditor.UI;
using UnityEngine;
using UnityEngine.UI;

[CustomEditor(typeof(SearchInputField))]
public class SearchInputFieldEditor : InputFieldEditor
{
    SerializedProperty m_Text;
    SerializedProperty m_TextComponent;
    SerializedProperty m_Placeholder;

    SerializedProperty m_Template;
    SerializedProperty m_Options;
    SerializedProperty m_OnMatched;

    protected override void OnEnable()
    {
        //base.OnEnable();

        m_Text = serializedObject.FindProperty("m_Text");
        m_TextComponent = serializedObject.FindProperty("m_TextComponent");
        m_Placeholder = serializedObject.FindProperty("m_Placeholder");
        m_Template = serializedObject.FindProperty("m_Template");
        m_Options = serializedObject.FindProperty("m_Options");
        m_OnMatched = serializedObject.FindProperty("m_OnMatched");
    }

    protected override void OnDisable()
    {
        //base.OnDisable();
    }

    public override void OnInspectorGUI()
    {
        //base.OnInspectorGUI();
        serializedObject.Update();
        
        EditorGUILayout.Space();
        EditorGUILayout.PropertyField(m_Text);

        EditorGUILayout.Space();
        EditorGUILayout.PropertyField(m_TextComponent);
        if (m_TextComponent != null && m_TextComponent.objectReferenceValue != null)
        {
            Text text = m_TextComponent.objectReferenceValue as Text;
            if (text.supportRichText)
            {
                EditorGUILayout.HelpBox("Using Rich Text with input is unsupported.", MessageType.Warning);
            }
        }
        EditorGUILayout.PropertyField(m_Placeholder);

        EditorGUILayout.Space();
        EditorGUILayout.PropertyField(m_Template);
        EditorGUILayout.PropertyField(m_Options, true); // 第二个参数必须为true，否则无法显示子节点即List内容
        EditorGUILayout.PropertyField(m_OnMatched);

        serializedObject.ApplyModifiedProperties();
    }
}

public static class CustomUIControls
{
    # region MenuOptions
    private const string kUILayerName = "UI";

    private const string kStandardSpritePath = "UI/Skin/UISprite.psd";
    private const string kBackgroundSpritePath = "UI/Skin/Background.psd";
    private const string kInputFieldBackgroundPath = "UI/Skin/InputFieldBackground.psd";
    private const string kKnobPath = "UI/Skin/Knob.psd";
    private const string kCheckmarkPath = "UI/Skin/Checkmark.psd";
    private const string kDropdownArrowPath = "UI/Skin/DropdownArrow.psd";
    private const string kMaskPath = "UI/Skin/UIMask.psd";

    private static  DefaultControls.Resources s_StandardResources;
    private static DefaultControls.Resources GetStandardResources()
    {
        if (s_StandardResources.standard == null)
        {
            s_StandardResources.standard = AssetDatabase.GetBuiltinExtraResource<Sprite>(kStandardSpritePath);
            s_StandardResources.background = AssetDatabase.GetBuiltinExtraResource<Sprite>(kBackgroundSpritePath);
            s_StandardResources.inputField = AssetDatabase.GetBuiltinExtraResource<Sprite>(kInputFieldBackgroundPath);
            s_StandardResources.knob = AssetDatabase.GetBuiltinExtraResource<Sprite>(kKnobPath);
            s_StandardResources.checkmark = AssetDatabase.GetBuiltinExtraResource<Sprite>(kCheckmarkPath);
            s_StandardResources.dropdown = AssetDatabase.GetBuiltinExtraResource<Sprite>(kDropdownArrowPath);
            s_StandardResources.mask = AssetDatabase.GetBuiltinExtraResource<Sprite>(kMaskPath);
        }
        return s_StandardResources;
    }

    [MenuItem("GameObject/UI/Custom/Search Input Field", false, 1900)]
    public static void AddSearchInputField(MenuCommand menuCommand)
    {
        GameObject searchInputField = CreateSearchInputField(GetStandardResources());

        Assembly assembly = Assembly.Load("UnityEditor.UI");
        Type type = assembly.GetType("UnityEditor.UI.MenuOptions");
        BindingFlags flag = BindingFlags.Static | BindingFlags.NonPublic;
        MethodInfo method = type.GetMethod("PlaceUIElementRoot", flag);
        method.Invoke(null, new object[] { searchInputField, menuCommand });
    }
    #endregion

    #region DefaultControls
    private const float kWidth = 160f;
    private const float kThickHeight = 30f;
    private const float kThinHeight = 20f;
    private static Vector2 s_ThickElementSize = new Vector2(kWidth, kThickHeight);
    //private static Vector2 s_ThinElementSize = new Vector2(kWidth, kThinHeight);
    //private static Vector2 s_ImageElementSize = new Vector2(100f, 100f);
    private static Color s_DefaultSelectableColor = new Color(1f, 1f, 1f, 1f);
    //private static Color s_PanelColor = new Color(1f, 1f, 1f, 0.392f);
    private static Color s_TextColor = new Color(50f / 255f, 50f / 255f, 50f / 255f, 1f);

    /// <summary>
    /// Create the basic UI search input field.
    /// </summary>
    /// <remarks>
    /// Hierarchy:
    /// (root)
    ///     SearchInputField
    ///         - PlaceHolder
    ///         - Text
    ///         - Template
    ///             - Viewport
    ///                 - Content
    ///                     - Item
    ///                         - Item Background
    ///                         - Item Label
    ///             - Scrollbar
    ///                 - Sliding Area
    ///                     - Handle
    /// <remarks>
    /// <param name="resources">The resources to use for creation.</param>
    /// <returns>The root GameObject of the created element.</returns>
    public static GameObject CreateSearchInputField(DefaultControls.Resources resources)
    {
        GameObject root = CreateUIElementRoot("SearchInputField", s_ThickElementSize);

        // InputField
        GameObject childPlaceholder = CreateUIObject("Placeholder", root);
        GameObject childText = CreateUIObject("Text", root);

        Image image = root.AddComponent<Image>();
        image.sprite = resources.inputField;
        image.type = Image.Type.Sliced;
        image.color = s_DefaultSelectableColor;

        SearchInputField inputField = root.AddComponent<SearchInputField>();
        inputField.targetGraphic = image;
        SetDefaultColorTransitionValues(inputField);

        Text text = childText.AddComponent<Text>();
        text.text = "";
        text.supportRichText = false;
        SetDefaultTextValues(text);

        Text placeholder = childPlaceholder.AddComponent<Text>();
        placeholder.text = "Enter text...";
        placeholder.fontStyle = FontStyle.Italic;
        // Make placeholder color half as opaque as normal text color.
        Color placeholderColor = text.color;
        placeholderColor.a *= 0.5f;
        placeholder.color = placeholderColor;

        RectTransform textRectTransform = childText.GetComponent<RectTransform>();
        textRectTransform.anchorMin = Vector2.zero;
        textRectTransform.anchorMax = Vector2.one;
        textRectTransform.sizeDelta = Vector2.zero;
        textRectTransform.offsetMin = new Vector2(10, 6);
        textRectTransform.offsetMax = new Vector2(-10, -7);

        RectTransform placeholderRectTransform = childPlaceholder.GetComponent<RectTransform>();
        placeholderRectTransform.anchorMin = Vector2.zero;
        placeholderRectTransform.anchorMax = Vector2.one;
        placeholderRectTransform.sizeDelta = Vector2.zero;
        placeholderRectTransform.offsetMin = new Vector2(10, 6);
        placeholderRectTransform.offsetMax = new Vector2(-10, -7);

        inputField.textComponent = text;
        inputField.placeholder = placeholder;

        // Template
        GameObject template = CreateUIObject("Template", root);
        GameObject viewport = CreateUIObject("Viewport", template);
        GameObject content = CreateUIObject("Content", viewport);
        GameObject item = CreateUIObject("Item", content);
        GameObject itemBackground = CreateUIObject("Item Background", item);
        GameObject itemLabel = CreateUIObject("Item Label", item);

        GameObject scrollbar = DefaultControls.CreateScrollbar(resources);
        scrollbar.name = "Scrollbar";
        SetParentAndAlign(scrollbar, template);

        Scrollbar scrollbarScrollbar = scrollbar.GetComponent<Scrollbar>();
        scrollbarScrollbar.SetDirection(Scrollbar.Direction.BottomToTop, true);

        RectTransform vScrollbarRT = scrollbar.GetComponent<RectTransform>();
        vScrollbarRT.anchorMin = Vector2.right;
        vScrollbarRT.anchorMax = Vector2.one;
        vScrollbarRT.pivot = Vector2.one;
        vScrollbarRT.sizeDelta = new Vector2(vScrollbarRT.sizeDelta.x, 0);

        // Setup item UI components.
        Text itemLabelText = itemLabel.AddComponent<Text>();
        SetDefaultTextValues(itemLabelText);
        itemLabelText.alignment = TextAnchor.MiddleLeft;

        Image itemBackgroundImage = itemBackground.AddComponent<Image>();
        itemBackgroundImage.color = new Color32(245, 245, 245, 255);
        
        Button itemButton = item.AddComponent<Button>();
        itemButton.targetGraphic = itemBackgroundImage;

        // Setup template UI components.

        Image templateImage = template.AddComponent<Image>();
        templateImage.sprite = resources.standard;
        templateImage.type = Image.Type.Sliced;

        ScrollRect templateScrollRect = template.AddComponent<ScrollRect>();
        templateScrollRect.content = (RectTransform)content.transform;
        templateScrollRect.viewport = (RectTransform)viewport.transform;
        templateScrollRect.horizontal = false;
        templateScrollRect.movementType = ScrollRect.MovementType.Clamped;
        templateScrollRect.verticalScrollbar = scrollbarScrollbar;
        templateScrollRect.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport;
        templateScrollRect.verticalScrollbarSpacing = -3;

        Mask scrollRectMask = viewport.AddComponent<Mask>();
        scrollRectMask.showMaskGraphic = false;

        Image viewportImage = viewport.AddComponent<Image>();
        viewportImage.sprite = resources.mask;
        viewportImage.type = Image.Type.Sliced;
        
        // Setting default Item list.
        itemLabelText.text = "Option A";
        inputField.AddOption("Option A");
        inputField.AddOption("Option B");
        inputField.AddOption("Option C");

        // Set up RectTransforms.
        RectTransform templateRT = template.GetComponent<RectTransform>();
        templateRT.anchorMin = new Vector2(0, 0);
        templateRT.anchorMax = new Vector2(1, 0);
        templateRT.pivot = new Vector2(0.5f, 1);
        templateRT.anchoredPosition = new Vector2(0, 2);
        templateRT.sizeDelta = new Vector2(0, 150);

        RectTransform viewportRT = viewport.GetComponent<RectTransform>();
        viewportRT.anchorMin = new Vector2(0, 0);
        viewportRT.anchorMax = new Vector2(1, 1);
        viewportRT.sizeDelta = new Vector2(-18, 0);
        viewportRT.pivot = new Vector2(0, 1);

        RectTransform contentRT = content.GetComponent<RectTransform>();
        contentRT.anchorMin = new Vector2(0f, 1);
        contentRT.anchorMax = new Vector2(1f, 1);
        contentRT.pivot = new Vector2(0.5f, 1);
        contentRT.anchoredPosition = new Vector2(0, 0);
        contentRT.sizeDelta = new Vector2(0, 28);

        RectTransform itemRT = item.GetComponent<RectTransform>();
        itemRT.anchorMin = new Vector2(0, 0.5f);
        itemRT.anchorMax = new Vector2(1, 0.5f);
        itemRT.sizeDelta = new Vector2(0, 20);

        RectTransform itemBackgroundRT = itemBackground.GetComponent<RectTransform>();
        itemBackgroundRT.anchorMin = Vector2.zero;
        itemBackgroundRT.anchorMax = Vector2.one;
        itemBackgroundRT.sizeDelta = Vector2.zero;

        RectTransform itemLabelRT = itemLabel.GetComponent<RectTransform>();
        itemLabelRT.anchorMin = Vector2.zero;
        itemLabelRT.anchorMax = Vector2.one;
        itemLabelRT.offsetMin = new Vector2(20, 1);
        itemLabelRT.offsetMax = new Vector2(-10, -2);

        template.SetActive(false);
        
        inputField.template = templateRT;

        return root;
    }

    private static GameObject CreateUIElementRoot(string name, Vector2 size)
    {
        GameObject child = new GameObject(name);
        RectTransform rectTransform = child.AddComponent<RectTransform>();
        rectTransform.sizeDelta = size;
        return child;
    }

    private static GameObject CreateUIObject(string name, GameObject parent)
    {
        GameObject go = new GameObject(name);
        go.AddComponent<RectTransform>();
        SetParentAndAlign(go, parent);
        return go;
    }

    private static void SetParentAndAlign(GameObject child, GameObject parent)
    {
        if (parent == null)
            return;

        child.transform.SetParent(parent.transform, false);
        SetLayerRecursively(child, parent.layer);
    }

    private static void SetLayerRecursively(GameObject go, int layer)
    {
        go.layer = layer;
        Transform t = go.transform;
        for (int i = 0; i < t.childCount; i++)
            SetLayerRecursively(t.GetChild(i).gameObject, layer);
    }

    private static void SetDefaultColorTransitionValues(Selectable slider)
    {
        ColorBlock colors = slider.colors;
        colors.highlightedColor = new Color(0.882f, 0.882f, 0.882f);
        colors.pressedColor = new Color(0.698f, 0.698f, 0.698f);
        colors.disabledColor = new Color(0.521f, 0.521f, 0.521f);
    }

    private static void SetDefaultTextValues(Text lbl)
    {
        // Set text values we want across UI elements in default controls.
        // Don't set values which are the same as the default values for the Text component,
        // since there's no point in that, and it's good to keep them as consistent as possible.
        lbl.color = s_TextColor;

        // Reset() is not called when playing. We still want the default font to be assigned
        //lbl.AssignDefaultFont();
    }
    #endregion
}
